import math
from Vertice import Vertice

class Grafo:
    """
    Inicializa o grafo e seus vértices.
    Recebe uma lista de valores posicionados dos vértices.
    """
    def __init__(self,vertices):
        self.vertices = self.addNodes(vertices)
        self.caminhoCritico = []
    
    def addNodes(self, listaVertices):
        vertices = []
        for vertice in listaVertices:   
            vertices.append(Vertice(vertice[0], vertice[1], vertice[2].split(',') if len(vertice)>2 else None))       # Gera cada nó.
        return vertices

    def calculaTemposEarliest(self):
        # inicioCedo = 0
        for vertice in self.vertices: # Para cada vértice,
            predecessores = vertice.getPredecessores() # obtém a lista de predecessores.
            fimCedoDosPredecessores = []

            for v in self.vertices:  # Para cada vértice,
                if predecessores and v.getIndice() in predecessores: # verifica se é um dos predecessores coletados antes.
                    fimCedoDosPredecessores.append(v.getFimCedo()) # Então, adiciona à lista o atributo fimCedo de cada predecessor.
            if fimCedoDosPredecessores:
                vertice.setInicioCedo(max(fimCedoDosPredecessores))
            vertice.setFimCedo(int(vertice.getInicioCedo()) + int(vertice.getDuracao()))

    def defineSucessores(self): 
        for vertice in self.vertices: # Para cada vértice,
            predecessores = vertice.getPredecessores() # obtém a lista de predecessores.

            for v in self.vertices:  # Para cada vértice,
                if predecessores and v.getIndice() in predecessores: # verifica se é um dos predecessores coletados antes.
                    v.addSucessor(vertice.getIndice())

    def calculaTemposLatest(self):
        self.defineSucessores()

        finsCedos = []

        # Percorre grafo para coletar todos os fimCedo dos vértices.
        for vertice in self.vertices:
            finsCedos.append(vertice.getFimCedo())

        fimCedoUltimaTarefa = max(finsCedos) # Separa o maior fimCedo.

        # Define o maior fimCedo como fimTarde de todas as tarefas sem sucessor (finais).
        for vertice in self.vertices:
            if not vertice.getSucessores():
                vertice.setFimTardio(fimCedoUltimaTarefa)

        for vertice in reversed(self.vertices): # Para cada vértice,
            sucessores = vertice.getSucessores() # obtém a lista de sucessores.
            inicioTardioDosSucessores = []

            for v in self.vertices:  # Para cada vértice,
                if sucessores and v.getIndice() in sucessores: # verifica se é um dos sucessores coletados antes.
                    inicioTardioDosSucessores.append(v.getInicioTardio()) # Então, adiciona à lista o atributo inicioTardio de cada sucessor.
            if inicioTardioDosSucessores:
                vertice.setFimTardio(min(inicioTardioDosSucessores))
            vertice.setInicioTardio(int(vertice.getFimTardio()) - int(vertice.getDuracao()))

    def calculaCaminhoCritico(self):
        for vertice in self.vertices:
            vertice.calculaFolga()
            if len(self.caminhoCritico) < 1 and vertice.getFolga() == 0: # Adicionaremos um primeiro vértice ao caminho crítico.
                self.caminhoCritico.append(vertice)
            elif vertice.getFolga() == 0 and (self.caminhoCritico[-1].getIndice() in vertice.getSucessores() or self.caminhoCritico[-1].getIndice() in vertice.getPredecessores()): # Adicionaremos os restantes, desde que ele seja um precedente ou sucessor do último adicionado.
                self.caminhoCritico.append(vertice)
        

    def getCaminhoCritico(self):
        return self.caminhoCritico

    def showNodes(self):
        return self.vertices